def validate_hash(hash, *required_opts)
  missed_opts = required_opts.reject{|opt| hash.key?(opt)}
  if missed_opts.size > 0
    raise ArgumentError, "Your hash should have the keys #{missed_opts.join}"
    return false
  end
  true
end
    

class Car
  attr_reader :model, :factory, :gas_limit, :gas, :km_run;
  
  class MaximumGasExceeded < StandardError; end
  class NoGasoline < StandardError; end
  
  def initialize(model, factory, opts)
    @model, @factory = model, factory;
    validate_hash(opts, :gas, :per_liter)
    @gas_limit = @gas = opts[:gas];
    @per_liter = opts[:per_liter];
    @km_run = 0;
  end
  
  def run(kms)
    gas_used = gas_used(kms);
    if gas_used > gas
      @km_run += kms_run(@gas);
      @gas = 0;
      raise NoGasoline, "Your gasonline tank is empty!";
    else
      @km_run += kms;
      @gas -= gas_used;
    end
  end  
  
  def fill(gas)
    avaible_gas_to_fill = @gas_limit - @gas;
    gas = avaible_gas_to_fill if gas == :up;
    if gas > avaible_gas_to_fill
      @gas = @gas_limit;
      raise MaximumGasExceeded, "Your tanks is already full!";
    else
      @gas += gas;
    end
  end
  
  private
  def gas_used(kms)
    kms / (@per_liter + 0.0);
  end
  
  def kms_run(liters)
    liters * (@per_liter + 0.0);
  end
    
end

class Factory
  attr_reader :name;
  
  def initialize(name)
    @name = name;
    @produced_cars = [];
  end
  
  def make_car(name, opts={})
    @produced_cars << Car.new(name, self, opts);
    @produced_cars.last;
  end
  
end

require "test/unit";

class CarsFactoriesTests < Test::Unit::TestCase
  
  def setup
    @volks = Factory.new "Volkswagen";
    @chev = Factory.new "Chevrolet";
  end
  
  def test_car_production
    golf = nil;
    assert_raises ArgumentError, "Producing a car withou options should render an ArgumentError" do
      @volks.make_car "Gol"
    end
    assert_nothing_raised "Producing a car with options shouldnt throw errors" do
      golf = @volks.make_car "Golf", :gas => 100, :per_liter => 30;
    end
    assert_equal golf.factory, @volks, "Golf should belong to Volkswagen";
    assert_not_equal golf.factory, @chev, "Golf should belong to Chevrolet";
  end
  
  def test_car_gas_and_running
    corv = @chev.make_car "Corvette", :gas => 60, :per_liter => 10;
    
    corv.run 25
    assert_equal corv.gas, 60 - (25 / 10.0), "Running 25km should decrease the gas in 2,5l";
    corv.run 30
    assert_equal corv.km_run, 55, "Running 25km and then 30km make the car to have 55 run kilometer";
    
    assert_raises Car::MaximumGasExceeded, "Filling up the tank that has 60 liter capacity with 90 liter should exceed the maximun limit"  do 
      corv.fill 90
    end
    
    corv.fill :up;
    assert_equal corv.gas_limit, corv.gas, "Filling up the gas should make the tank full";
    
    assert_raises Car::NoGasoline, "Running more than a tank can provide should result in error" do
      corv.run 800
    end
  end
  
end

